package panda.server.util.datamine;

import java.io.BufferedInputStream;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import panda.client.enumerations.MagicSet;

/**
 * Creates a wget batch file to extract english magic:the gathering card scans from magiccards.info, a popular online
 * magic card database. Retrieves card (oracle) information from magiccards.info.
 * 
 * @author zane.tian@gmail.com
 */
public class MagiccardsInfoExtractor {
	static final boolean DEBUG = false;

	public static void main(String[] args) throws IOException {
		List<MagicSet> sets = new ArrayList<MagicSet>();
		MagiccardsInfoExtractor mie = new MagiccardsInfoExtractor();

		sets.add(MagicSet.SCARS_OF_MIRRODIN);

		for(MagicSet set : sets) {
			String setName = set.getName().replace(":", ""); /* fix for Ravnica since Windows doesn't allow ":" in filenames */
			writeBatchFile("C:/Users/kyruru/Desktop/" + setName + ".bat", set.getTotalCards(), set);
			// writeBatchFile("/Users/kyruru/Desktop/scripts/" + set.getAbbreviation() + ".sh", set.getTotalCards(), set);

			mie.getCardInfo(set);
			// convertMtgsTextSpoiler();
		}
	}

	/**
	 * Reads html files from magiccards.info online sequentially, obtaining card information (casting cost, card text,
	 * illustrator, flavor text, card type) from card numbers via http://magiccards.info/setName/en/n.html
	 * 
	 * @param set
	 */
	public void getCardInfo(MagicSet set) {
		String root = "http://magiccards.info/" + set.getAbbrev() + "/en/";
		StringBuffer buffer = new StringBuffer();

		URL link = null;
		try {
			for(int i = 1; i <= set.getTotalCards(); i++) {
				link = new URL(root + i + ".html");

				InputStream in = link.openStream();

				BufferedInputStream stream = new BufferedInputStream(in);
				Scanner scanner = new Scanner(stream, "UTF8");

				buffer = new StringBuffer();
				while(scanner.hasNext()) {
					String line = scanner.nextLine();
					buffer.append(line);
				}

				final String pageSrcWithTabs = buffer.toString();
				final String pageSrc = pageSrcWithTabs.replace("\t", "").replace("    ", "");

				/**
				 * <ul>
				 * <li>Creature - Angel 3/3, 4W (5)
				 * <li>Flying <i>(This creature can't be blocked except by creatures with flying.)</i>
				 * <li>When Angel of Mercy comes into play, you gain 3 life.
				 * <li><i>A song of life soars over fields of blood.</i>
				 * <li>Illus. Melissa A. Benson
				 * </ul>
				 * Splitting card text into components: The card text is split by "<br />
				 * " tags. The first line contains the casting cost (if the card isn't a land), the type, and the power +
				 * toughness if the card is a creature. The last line contains the illustrator's name, and the lines in
				 * between is the card's body text.
				 */				
				
				String nameRegex = "<a href=\"/" + set.getAbbrev() + "/en/" + i + ".html\">(.*?)</a>";
				String typeRegex = "<p>(.*?)</p>";
				String bodyTextRegex = "<p class=\"ctext\">(.*?)</p>";
				String flavorTextRegex = "<p>(.*?)</p>";
				String illustratorRegex = "<p>Illus. (.*?)</p>";
				String rarityRegex = "<b>" + set.getName() + " (?:(.*?))</b>";
				
				/* the resulting snippets of card information we want */
				String name, typeLine, bodyText /* includes flavor */, illustrator, rarity;
				
				Pattern pattern = Pattern.compile(nameRegex + ".*?" + typeRegex + bodyTextRegex + flavorTextRegex + illustratorRegex + ".*?" + rarityRegex); /* ignore whitespace */
				Matcher matcher = pattern.matcher(pageSrc);
				
				if(matcher.find()) {
					name = matcher.group(1);
					typeLine = matcher.group(2);
					bodyText = matcher.group(3).replace("<b>", "").replace("</b>", "") + "<br />" + matcher.group(4);
					illustrator = matcher.group(5);
					rarity = matcher.group(6).substring(1, matcher.group(6).length() - 1);
				} else {
					throw new IllegalStateException();
				}
				
				/**
				 * Casting cost and type: all found in the first line. Creature - Human Wizard 1/1, U (1)
				 */
				final int commaPosition;
				final String type;
				final String castingCost;

				if(typeLine.contains(",")) {
					commaPosition = typeLine.indexOf(',');
					type = typeLine.substring(0, commaPosition);
					castingCost = typeLine.substring(commaPosition + 2);
				} else {
					castingCost = "";
					type = typeLine;
				}

				if(DEBUG) {
					System.out.println("Casting Cost: " + castingCost + ".");
					System.out.println("Type: " + type + ".");
				}

				StringBuffer javaLine = new StringBuffer();
				javaLine.append("cards.add(new CardData(set, \"");
				javaLine.append(name + "\", ");
				javaLine.append("\"" + castingCost + "\", ");
				javaLine.append("\"" + type + "\", ");
				javaLine.append("\"" + bodyText.replace("\"", "\\\"") + "\", ");

				// javaLine.append("\"" + rarity + "\", ");
				if(rarity.equals("Land") || rarity.equals("Basic Land")) {
					javaLine.append("CardRarity.LAND, ");
				} else if(rarity.toLowerCase().equals("mythic rare")) {
					javaLine.append("CardRarity.MYTHIC, ");
				} else if(rarity.toLowerCase().equals("common")) {
					javaLine.append("CardRarity.COMMON, ");
				} else if(rarity.toLowerCase().equals("uncommon")) {
					javaLine.append("CardRarity.UNCOMMON, ");
				} else if(rarity.toLowerCase().equals("rare")) {
					javaLine.append("CardRarity.RARE, ");
				} else if(rarity.toLowerCase().equals("special")) {
					javaLine.append("CardRarity.SPECIAL, ");
				} else {
					throw new IllegalStateException("Incompatible rarity encountered: " + rarity + " from " + name);
				}

				javaLine.append("\"" + illustrator + "\", ");
				javaLine.append(i + "));");

				System.out.println(javaLine);

				in.close();
			}
		} catch(MalformedURLException e) {
			e.printStackTrace();
		} catch(IOException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * Converts the MTGSalvation text spoiler into a rudimentary MagicCardDB entry.
	 * Last used for Rise of the Eldrazi, before the prerelease when the visual spoiler was released
	 * on April 14th, Wednesday 2010.
	 * 
	 * Note: This is after I replaced all instances of <b>Name:</b> with just Name:
	 */
	public static void convertMtgsTextSpoiler() {
		String name = "";
		String castingCost = "0";
		String type = "none";
		String bodyText = "none";
		String rarity = "";
		String illustrator = "none";
		
		/* iterate through text spoiler, adding names and rarities to 2 separate but in-sync lists */
		List<String> names = new ArrayList<String>();
		List<String> rarities = new ArrayList<String>();
		
		StringBuffer buffer = new StringBuffer();
		try {
			Scanner scanner = new Scanner(new File("C:/Users/kyruru/Desktop/spoiler.txt"), "ISO-8859-1"); /* TODO: why not UTF8? */
			/* convert each text file line into a deckentry. skip the sideboard */
			while(scanner.hasNext()) {
				buffer.append(scanner.nextLine());
			}
		} catch(Exception ex) {
			ex.printStackTrace();
		}
		
		String nameRegex = "<b>(.*?)</b>";
		String rarityRegex = "Rarity:\\s*?</td>\\s*?<td>\\s*?(\\?|Mythic Rare|Rare|Uncommon|Common|Land|Basic Land)\\s*?</td>";
		
		Pattern pattern = Pattern.compile(nameRegex + ".*?" + rarityRegex);
		Matcher matcher = pattern.matcher(buffer);
		
		while(matcher.find()) {
			System.out.println(matcher.group(1));
			// System.out.println(matcher.group(2));
			names.add(matcher.group(1));
			rarities.add(matcher.group(2));
		}
		
		assert names.size() == rarities.size();
		
		for(int i = 0; i < names.size(); i++) {
			name = names.get(i);
			rarity = rarities.get(i);
			
			StringBuffer javaLine = new StringBuffer();
			javaLine.append("cards.add(new CardData(set, \"");
			javaLine.append(name + "\", ");
			javaLine.append("\"" + castingCost + "\", ");
			javaLine.append("\"" + type + "\", ");
			javaLine.append("\"" + bodyText.replace("\"", "\\\"") + "\", ");

			// javaLine.append("\"" + rarity + "\", ");
			if(rarity.equals("Land") || rarity.equals("Basic Land")) {
				javaLine.append("CardRarity.LAND, ");
			} else if(rarity.toLowerCase().equals("mythic rare")) {
				javaLine.append("CardRarity.MYTHIC, ");
			} else if(rarity.toLowerCase().equals("common")) {
				javaLine.append("CardRarity.COMMON, ");
			} else if(rarity.toLowerCase().equals("uncommon")) {
				javaLine.append("CardRarity.UNCOMMON, ");
			} else if(rarity.toLowerCase().equals("rare")) {
				javaLine.append("CardRarity.RARE, ");
			} else if(rarity.toLowerCase().equals("special")) {
				javaLine.append("CardRarity.SPECIAL, ");
			} else {
				 // throw new IllegalStateException("Incompatible rarity encountered: " + rarity + " from " + name);
				System.err.println("Incompatible rarity encountered: " + rarity + " from " + name);
				javaLine.append("CardRarity.COMMON, ");
			}

			javaLine.append("\"" + illustrator + "\", ");
			javaLine.append((i+1) + "));");

			System.out.println(javaLine);
		}
	}
	
	/**
	 * Outputs wget commands to a script file for all (totalCardsInSet) cards in the set. totalCardsInSet includes
	 * duplicates.
	 * 
	 * @param filename
	 * @param totalCardsInSet
	 * @param set
	 */
	public static void writeBatchFile(String filename, int totalCardsInSet, MagicSet set) {
		// final String wgetPrefix = "wget -P/Users/kyruru/Desktop/scans/";
		final String wgetPrefix = "C:/GnuWin32/bin/wget -N -PC:/Users/kyruru/Desktop/";
		final String sitePrefix = "http://magiccards.info/scans/en/";

		try {
			BufferedWriter out = new BufferedWriter(new FileWriter(filename));
			for(int i = 0; i < totalCardsInSet; i++) {
				out.write(wgetPrefix + set.getAbbrev() + "\\ " + sitePrefix + set.getAbbrev() + "/" + (i + 1) + ".jpg");
				out.newLine();
			}

			out.close();
		} catch(IOException e) {
			e.printStackTrace();
		}

		if(DEBUG) {
			System.out.println(set + " complete! " + set.getTotalCards() + " cards processed");
		}
	}
}